package browser

import (
	"context"

	"gitee.com/baixudong/gospider/requests"
	"gitee.com/baixudong/gospider/tools"
)

type RequestOption struct {
	Url      string            `json:"url"`
	Method   string            `json:"method"`
	PostData string            `json:"postData"`
	Headers  map[string]string `json:"headers"`
}
type RequestData struct {
	Url              string            `json:"url"`
	UrlFragment      string            `json:"urlFragment"`
	Method           string            `json:"method"`
	Headers          map[string]string `json:"headers"`
	PostData         string            `json:"postData"`
	HasPostData      bool              `json:"hasPostData"`
	MixedContentType string            `json:"mixedContentType"`
	InitialPriority  string            `json:"initialPriority"` //初始优先级
	ReferrerPolicy   string            `json:"referrerPolicy"`
	IsLinkPreload    bool              `json:"isLinkPreload"`   //是否通过链路预加载加载。
	PostDataEntries  []DataEntrie      `json:"postDataEntries"` //是否通过链路预加载加载。
}
type RouteData struct {
	RequestId    string      `json:"requestId"`
	Request      RequestData `json:"request"`
	FrameId      string      `json:"frameId"`
	ResourceType string      `json:"resourceType"`
	NetworkId    string      `json:"networkId"`

	ResponseErrorReason string   `json:"responseErrorReason"`
	ResponseStatusCode  int      `json:"responseStatusCode"`
	ResponseStatusText  string   `json:"responseStatusText"`
	ResponseHeaders     []Header `json:"responseHeaders"`
}

type Route struct {
	webSock  *webSock
	recvData RouteData
	reqCli   *requests.Client
}

func (obj *Route) NewRequestOption() RequestOption {
	headers := obj.Headers()
	if headers["Accept-Language"] == "" {
		headers["Accept-Language"] = "zh-CN,zh;q=0.9"
	}
	return RequestOption{
		Url:      obj.Url(),
		Method:   obj.Method(),
		PostData: obj.PostData(),
		Headers:  obj.Headers(),
	}
}
func (obj *Route) Url() string {
	return obj.recvData.Request.Url
}
func (obj *Route) Method() string {
	return obj.recvData.Request.Method
}
func (obj *Route) PostData() string {
	return obj.recvData.Request.PostData
}
func (obj *Route) Headers() map[string]string {
	return obj.recvData.Request.Headers
}

func (obj *Route) Request(ctx context.Context, routeOption RequestOption, options ...requests.RequestOption) (FulData, error) {
	var option requests.RequestOption
	if len(options) > 0 {
		option = options[0]
	}
	if routeOption.PostData != "" {
		option.Bytes = tools.StringToBytes(routeOption.PostData)
	}
	option.Headers = routeOption.Headers

	var fulData FulData
	var err error
	var proxy string

	if option.Proxy != "" {
		proxy = option.Proxy
	} else if obj.reqCli.Proxy != "" {
		proxy = obj.reqCli.Proxy
	}

	if err = obj.webSock.db.get(routeOption, &fulData, proxy); err == nil && fulData.StatusCode < 400 && fulData.StatusCode > 100 {
		return fulData, err
	}
	rs, err := obj.reqCli.Request(ctx, routeOption.Method, routeOption.Url, option)
	if err != nil {
		return fulData, err
	}
	headers := []Header{}
	for kk, vvs := range rs.Headers() {
		for _, vv := range vvs {
			headers = append(headers, Header{
				Name:  kk,
				Value: vv,
			})
		}
	}
	fulData.StatusCode = rs.StatusCode()
	fulData.Body = rs.Text()
	fulData.Headers = headers

	if fulData.StatusCode < 400 && fulData.StatusCode > 100 {
		obj.webSock.db.put(routeOption, proxy, fulData, 60*5)
	}
	return fulData, nil
}

func (obj *Route) FulFill(ctx context.Context, fulData FulData) error {
	params := map[string]any{
		"requestId":    obj.recvData.RequestId,
		"responseCode": fulData.StatusCode,
	}
	if fulData.Headers != nil {
		params["responseHeaders"] = fulData.Headers
	}
	if fulData.Body != "" {
		params["body"] = tools.Base64Encode(fulData.Body)
	}
	_, err := obj.webSock.send(ctx, commend{
		Method: "Fetch.fulfillRequest",
		Params: params,
	})
	return err
}
func (obj *Route) Continue(ctx context.Context) error {
	_, err := obj.webSock.send(ctx, commend{
		Method: "Fetch.continueRequest",
		Params: map[string]any{
			"requestId": obj.recvData.RequestId,
		},
	})
	return err
}

// Failed, Aborted, TimedOut, AccessDenied, ConnectionClosed, ConnectionReset, ConnectionRefused, ConnectionAborted, ConnectionFailed, NameNotResolved, InternetDisconnected, AddressUnreachable, BlockedByClient, BlockedByResponse
func (obj *Route) Fail(ctx context.Context, errorReason string) error {
	_, err := obj.webSock.send(ctx, commend{
		Method: "Fetch.failRequest",
		Params: map[string]any{
			"requestId":   obj.recvData.RequestId,
			"errorReason": errorReason,
		},
	})
	return err
}

type Header struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type FulData struct {
	StatusCode int      `json:"responseCode"`
	Headers    []Header `json:"responseHeaders"`
	Body       string   `json:"body"`
}
